/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package alggen;

import java.awt.Point;
import java.util.Random;
/**
 *
 * @author Macias
 */
public class Algorytm {
  public int [][] mapa;
  public Point startPoz,endPoz;   //punkty startu i konca
  public int maxPokolenieCount;      //maksymalna ilosc pokolen w algorytmie - warunek stopu
  public int pokolenieID;         //ktore pokolenie obecnie zyje
  public int iloscPunktowPos;     //przez ile punktow przejezdza nasz robot
  public int wielkoscPopulacji;   //ile osobnikow jest w danej populacji
  public int populacjiNaSkok;     //ile populacji jest obliczana przy kazdym wywolaniu funkcji obliczKrok()
  public boolean stopAlgorytm = false;
  public float G0 = 1f;
  public float GBeta = 0.1f;
  public boolean useR2 = false;
  
  public int maxThreads = 2;

  Osobnik najlepszyOsobnik ;
  Osobnik najgorszyOsobnik ;
  int aktywnychOsobnikow;
  Osobnik[] osobniki;
  Wyniki wyniki;

  public float prawdCross;      //prawdopodobnienstwo krzyzowania
  public float prawdMut;        //prawdopodobienstwo mutacji

    Algorytm(Wyniki wyniki){
        this.wyniki = wyniki;
        
        startPoz= new Point(0,0);
        endPoz= new Point(255,255);
        maxPokolenieCount=100;
        pokolenieID=-1;
        iloscPunktowPos=5;
        wielkoscPopulacji=50;
        populacjiNaSkok = 1;
//        najlepszyO    sobnik = new Osobnik();
//        for(int i=0;i<20;i++)
//            najlepszyOsobnik.sciezka[i] = new Point((10*i), (int) (0.5*(i*i)));

    }

    public void inicjalizuj(){
        float najlepszyWynik = 0;
        float najgorszyWynik = 0;
        //losoj poczatkowa populacje
        osobniki = new Osobnik[wielkoscPopulacji];
        Random rand= new Random();
        najlepszyOsobnik=null;
        najgorszyOsobnik = null;
        stopAlgorytm = false;
        aktywnychOsobnikow = osobniki.length;

        pokolenieID = 0;
        for(int i=0;i<wielkoscPopulacji;i++){
            osobniki[i]=new Osobnik(startPoz,endPoz,mapa);
            osobniki[i].sciezka = new Point[iloscPunktowPos];
            for(int j=0;j<iloscPunktowPos;j++){
                osobniki[i].sciezka[j] = new Point(rand.nextInt(256),rand.nextInt(256));
            }
            for(int j=0;j<iloscPunktowPos*2;j++){
                osobniki[i].v[j] = 0;
            }
            osobniki[i].obliczPrzystosowanie();
            
            if(osobniki[i].wartPrzyst>najgorszyWynik)
                najgorszyWynik = osobniki[i].wartPrzyst;
            if(osobniki[i].wartPrzyst<najlepszyWynik || i==0)
                najlepszyWynik = osobniki[i].wartPrzyst;
        }
        
        for(int i=0;i<wielkoscPopulacji;i++){
            osobniki[i].mass = (osobniki[i].wartPrzyst-najgorszyWynik) / (najlepszyWynik-najgorszyWynik) + 0.01f;
            //System.out.println(i+"   "+osobniki[i].mass);
        }
    }

    Osobnik obliczKrok(){
        for(int i = 0; i<populacjiNaSkok && !stopAlgorytm; i++)
            iteruj();
//        System.out.println("################ wyswietlam");
        return najlepszyOsobnik;
    }

    private void iteruj(){
        float G;
        float ro;
        float alfa;
        float r;
        float najlepszyWynik = 0;
        float najgorszyWynik = 0;
//        Synch synch = new Synch(this);
        pokolenieID++;
        Random rand= new Random();
        
        G = G0 * (float)Math.exp(-Math.pow(2*pokolenieID/maxPokolenieCount, 2));

        for(int i = 0; i<osobniki.length; i++){     //modyfikacja wszystkich osobnikow
            osobniki[i].F = new float[iloscPunktowPos*2];
            osobniki[i].a = new float[iloscPunktowPos*2];
            
            for(int j = 0; j<osobniki[i].F.length; j++){
                osobniki[i].F[j] = 0;
                osobniki[i].a[j] = 0;
            }

            for(int j = 0; j<osobniki.length; j++){ //obliczanie sily wypadkowej
                ro = 0.4f + 0.2f*rand.nextFloat();
                if(j == i)
                    continue;
                if(!osobniki[j].alive)
                    continue;
                if(osobniki[i].mass > osobniki[j].mass)
                    continue;
                r = 0;
                for(int n = 0; n<iloscPunktowPos; n++){
                    r += Math.pow(osobniki[j].sciezka[n].x - osobniki[i].sciezka[n].x, 2);
                    r += Math.pow(osobniki[j].sciezka[n].y - osobniki[i].sciezka[n].y, 2);
                }

                if(!useR2)
                    r = (float)Math.sqrt(r);
//                r = (float)Math.pow(r, 0.5);
//                System.out.println(i+","+j+": r =   "+r);

                for(int n = 0; n<osobniki[i].F.length/2; n++){
                    osobniki[i].F[2*n] += ro * G * osobniki[j].mass * osobniki[i].mass * (osobniki[j].sciezka[n].x - osobniki[i].sciezka[n].x) / (r+1f);
                    osobniki[i].F[2*n+1] += ro * G * osobniki[j].mass * osobniki[i].mass * (osobniki[j].sciezka[n].y - osobniki[i].sciezka[n].y) / (r+1f);
//                    System.out.println(i+","+j+": ( "+osobniki[j].sciezka[n].x+", "+osobniki[j].sciezka[n].y+") - ( "+osobniki[i].sciezka[n].x+", "+osobniki[i].sciezka[n].y+") => F("+n+") =   "+osobniki[i].F[2*n] + ",  "+osobniki[i].F[2*n+1]);
                }
            }

//            for(int n = 0; n<osobniki[i].F.length/2; n++){
//                System.out.println(i+": F("+n+") =   "+osobniki[i].F[2*n] + ",  "+osobniki[i].F[2*n+1]);
//            }
        }

        for(int i = 0; i<osobniki.length; i++){
            if(!osobniki[i].alive || osobniki[i] == najlepszyOsobnik)
                continue;
            alfa = 0.4f + 0.2f*rand.nextFloat();
            for(int n = 0; n<osobniki[i].F.length/2; n++){
                osobniki[i].a[2*n] = osobniki[i].F[2*n]/(osobniki[i].mass);
                osobniki[i].a[2*n+1] = osobniki[i].F[2*n+1]/(osobniki[i].mass);

//                System.out.println(i+":   a =  "+osobniki[i].a[2*n] + ",  "+osobniki[i].a[2*n+1]);
//                System.out.println(i+":   a =  "+osobniki[i].a[2*n] + ",  "+osobniki[i].a[2*n+1]);

                osobniki[i].v[2*n] = alfa*osobniki[i].v[2*n] + osobniki[i].a[2*n];
                osobniki[i].v[2*n+1] = alfa*osobniki[i].v[2*n+1] + osobniki[i].a[2*n+1];

//                System.out.println(i+":   V =  "+osobniki[i].v[2*n] + ",  "+osobniki[i].v[2*n+1]);

                osobniki[i].sciezka[n].x += osobniki[i].v[2*n];
                osobniki[i].sciezka[n].y += osobniki[i].v[2*n+1];

                //zapewnienie, ze rozwiazanie bedzie dopuszczalne
                if(osobniki[i].sciezka[n].x<0)
                    osobniki[i].sciezka[n].x = 0;
                if(osobniki[i].sciezka[n].x>255)
                    osobniki[i].sciezka[n].x = 255;
                if(osobniki[i].sciezka[n].y<0)
                    osobniki[i].sciezka[n].y = 0;
                if(osobniki[i].sciezka[n].y>255)
                    osobniki[i].sciezka[n].y = 255;
            }
        }

        najlepszyWynik = -1;
        najgorszyWynik = -1;
        for (int i = 0 ;i<wielkoscPopulacji-1 ; i++){
            osobniki[i].obliczPrzystosowanie();
            if(!osobniki[i].alive){
                continue;
            }
            if( najgorszyWynik<0 || osobniki[i].wartPrzyst>najgorszyWynik )
                najgorszyWynik = osobniki[i].wartPrzyst;
            if( najlepszyWynik<0 || osobniki[i].wartPrzyst<najlepszyWynik || i==0 )
                najlepszyWynik = osobniki[i].wartPrzyst;
        }

        for(int i=0;i<wielkoscPopulacji;i++)
            osobniki[i].mass = (osobniki[i].wartPrzyst-najgorszyWynik) / (najlepszyWynik-najgorszyWynik) + 0.01f;

        wyniki.wyniki[pokolenieID-1][0]=0;
        wyniki.wyniki[pokolenieID-1][1]=0;
        wyniki.wyniki[pokolenieID-1][2]=0;

        najgorszyOsobnik = null;
        for(int i=0; i<osobniki.length;i++){
            if(!osobniki[i].alive)
                continue;
//            System.out.println("WartPrzyst = "+osobniki[i].wartPrzyst + ",  masa:  "+osobniki[i].mass);
            if(najlepszyOsobnik == null || najlepszyOsobnik.wartPrzyst>osobniki[i].wartPrzyst){
                System.err.println("Old best: "+najlepszyOsobnik+",  new best: " + osobniki[i]);
                najlepszyOsobnik=osobniki[i];
            }
            if(najgorszyOsobnik == null || najgorszyOsobnik.wartPrzyst<osobniki[i].wartPrzyst)
                najgorszyOsobnik=osobniki[i];
            if(wyniki.wyniki[pokolenieID-1][0]==0 || wyniki.wyniki[pokolenieID-1][0]<osobniki[i].wartPrzyst)
                wyniki.wyniki[pokolenieID-1][0] = osobniki[i].wartPrzyst;
            if(wyniki.wyniki[pokolenieID-1][2]==0 || wyniki.wyniki[pokolenieID-1][2]>osobniki[i].wartPrzyst)
                wyniki.wyniki[pokolenieID-1][2] = osobniki[i].wartPrzyst;
        }

        System.out.println("Przystosowanie najlepszego osobnika: " + najlepszyOsobnik.wartPrzyst + ",  masa:  "+najlepszyOsobnik.mass);
        System.out.println("Przystosowanie najgorszego osobnika: " + najgorszyOsobnik.wartPrzyst + ",  masa:  "+najgorszyOsobnik.mass);
            
        wyniki.wyniki[pokolenieID-1][1] = najlepszyOsobnik.wartPrzyst;
        if(wyniki.wyniki[pokolenieID-1][0]>wyniki.worst)
            wyniki.worst = wyniki.wyniki[pokolenieID-1][0];
        if(wyniki.best==-1 || wyniki.wyniki[pokolenieID-1][2]<wyniki.best)
            wyniki.best = wyniki.wyniki[pokolenieID-1][2];
        if(wyniki.worstFromBest==-1 || wyniki.wyniki[pokolenieID-1][1]>wyniki.worstFromBest)
            wyniki.worstFromBest = wyniki.wyniki[pokolenieID-1][1];
        wyniki.validCount++;
//        System.out.println("to jest "+ pokolenieID+"pokolenie");

        if((float)aktywnychOsobnikow/osobniki.length > (float)pokolenieID/maxPokolenieCount && aktywnychOsobnikow>1 && najgorszyOsobnik != najlepszyOsobnik){
            najgorszyOsobnik.alive = false;
            aktywnychOsobnikow--;
            System.out.println("Usuwam osobnika. Zostalo: "+aktywnychOsobnikow);
        }
    }

    class Worker implements Runnable{
        
       Osobnik[] osobniki = null;
       Synch synch = null;
       int from = 0;
       int to = 0;
       
       public Worker(Synch synch, Osobnik[] osobniki, int from, int to){
           this.osobniki = osobniki;
           this.synch = synch;
           this.from = from;
           this.to = to;
           synch.add();
       }

        public void run() {
            for(int i = from; i<=to; i++)
                osobniki[i].obliczPrzystosowanie();
            synch.sub();
        }
    }
    
    class Synch{
        
        final Object lock = new Object();
        int count = 0;
        final Object parent;
        
        public Synch(Object parent){
            this.parent = parent;
        }
        
        public void add(){
            synchronized(lock){
                count++;
            }
        }
        
        public void sub(){
            int c = 0;
            synchronized(lock){
                count--;
                c = count;
            }
            if(c == 0){
                synchronized(parent){
                    parent.notify();
                }
            }
        }
        
        public int get(){
            int c;
            synchronized(lock){
                c = count;
            }
            return c;
        }
    }

}
